// 创建列表类
function ArrayList() {
    // 属性
    this.array = [];

    // 方法
    // 1 插入数据
    ArrayList.prototype.insert = function (valueItem) {  
        this.array.push(valueItem);
    }

    // 2 toString方法
    ArrayList.prototype.toString = function () {  
        return this.array.join(' -> ');
    }

    // 交换数据
    ArrayList.prototype.swap = function (m, n) {  
        let temp = this.array[m];
        this.array[m] = this.array[n];
        this.array[n] = temp;
    }

    // 3 冒泡排序 : 复杂度 O(N^2) 真实交换次数: N(N-1)/4
    ArrayList.prototype.bubbleSort = function () {
        // 获取数组的长度
        let length = this.array.length;

        // 两层循环,进行冒泡排序
        for (let i = length-1; i >= 0 ; i--) {
            // 此处的i为需要冒泡的长度
            for (let j = 0; j < i; j++) {
                // 判断是否需要互换
                if (this.array[j] > this.array[j+1]) {
                    // 元素互换
                    this.swap(j, j+1)
                }                
            }
        }
    }

    // 4 选择排序 : 复杂度 O(N^2) 真实交换次数: N-1
    ArrayList.prototype.selectSort = function () {  
        let length = this.array.length;

        for (let i = 0; i < length - 1; i++) {
            // 寻找当前最小值
            let mid = i;
            for (let j = i +1; j <length; j++) {
                if (this.array[mid] > this.array[j]) {
                    mid = j;
                }
            }
            // 最小值与起始位置进行交换
            this.swap(i, mid);
        }
    }

    // 5 插入排序 : 复杂度 O(N^2)
    ArrayList.prototype.insertSort = function () {  
        let length = this.array.length;
        for (let index = 1; index < length; index++) {
            // 内存循环:获取i位置的元素,和前面的数据进行比较
            let temp = this.array[index];
            let i = index;
            while (this.array[i-1] > temp && i>0) {
                this.array[i] = this.array[i-1];
                i--;
            }
            // 此处i已经做了i--,遍历到了最后需要更改地方
            this.array[i] = temp;
        }
    }

    // 7 希尔排序(升级版插入排序) : 最坏为 O(N^2) 通常好于 O(N^2)
    ArrayList.prototype.shellSort = function () {
        let length = this.array.length;

        // 获取初始间距
        let gap = Math.floor(length/2);
        // 当间隔为0时,停止遍历
        while (gap >= 1) {
            for (let index = gap; index < length; index++) {
                // 记录小分组中初始插入排序位置
                let temp = this.array[index];
                let i = index;
                // 小分组中开始插入排序
                while (temp < this.array[i - gap] && i >= gap) {
                    // this.swap(i, i-gap); // 并不高效
                    this.array[i] = this.array[i-gap];
                    i -= gap;
                }
                this.array[i] = temp;
            }
            gap = Math.floor(gap/2);
        }
    }

    // 8 快速排序 (升级版 冒泡排序) : 平均效率 O(N * logN)
    // 1 选择枢纽
    ArrayList.prototype.median = function (left, right) {  
        // 1 取出中间值
        let center = Math.floor((left + right)/2);

        // 三数排序
        if (this.array[left] > this.array[center]) {
            this.swap(left, center)
        }
        if (this.array[center] > this.array[right]) {
            this.swap(center, right)
        }
        if (this.array[left] > this.array[center]) {
            this.swap(left, center)
        }

        // 将中间值与倒数第二个进行互换
        this.swap(center, right-1)

        // 此时倒数第二个为中间枢纽,将枢纽返回
        return this.array[right-1]
    }

    // 2 递归遍历
    ArrayList.prototype.quickItem = function (left, right) {
        // 1 结束条件 左指针大于右指针或者重合,则结束遍历
        if (left >= right) return

        // 2 获取枢纽
        let pivot = this.median(left, right);

        // 定义子指针变量
        let i = left;
        let j = right - 1; //倒数第二个,故减一

        // 开始互换
        while (true) {
            // 左子指针开始移动
            while (this.array[++i] < pivot) { }
            // 右子指针开始移动
            while (this.array[--j] > pivot) { }

            // 在数组内则互换,若是指针重合或是指针错位,则直接将指针较大的数(或是重合)与枢纽互换(此时较大为i).
            if (i < j) {
                // 符合条件则互换
                this.swap(i, j);
            } else {
                // 退出遍历,将枢纽与较大值互换
                break
            }
        }

        // 枢纽互换,即将枢纽放回正确的位置
        this.swap(i, right -1);

        // 分而治之,开始递归
        this.quickItem(left, i-1);  // 左半部分
        this.quickItem(i + 1, right)  //右半部分
    }

    // 封装快速遍历
    ArrayList.prototype.quickSort = function () {
        this.quickItem(0, this.array.length - 1)
    }
}

// 测试
let list = new ArrayList();

let value = [99, 2, 3, 4, 35, 12, 44, 23, 33, 44, 7, 54, 99, 3, 35];
// 插入元素
for (let index = 0; index < value.length; index++) {
    list.insert(value[index]);    
}

console.log("初始化列表:" + list.toString());
// list.bubbleSort();
// console.log("冒泡排序:  " + list.toString());
// list.selectSort();
// console.log("选择排序:  " + list.toString());
// list.insertSort();
// console.log("插入排序:  " + list.toString());
// list.shellSort();
// console.log("希尔排序:  " + list.toString());
list.quickSort();
console.log("快速排序:  " + list.toString());